// View.cpp
#include <iostream>
#include <sstream>
#include "View2D.h"
#include "config.h"
#include "PacMan.h"
#include <memory>

using namespace std;

View2D* View2D::instance = 0;

/*
 * Constructeur de la vue 2D qui initialise tous les attributs
 * nécessaires.
 */
View2D::View2D() :
		View() {

	initDisplayList();
}

/*
 * Pattern singleton
 */
View2D *View2D::getInstance() {
	if (View2D::instance == 0)
		instance = new View2D();
	return instance;
}

void View2D::viewPortMiddle() { //view contenant le jeu
	// la viewport1 prend 2.5/5 de la fênetre
	glViewport(0, (h_global * 1.5) / 5, w_global, (h_global * 2.5) / 5); // Taille et positionnement de la viewport
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity(); // Initialisation de la matrice de projection
	/*gérer distortion*/
	distortionMiddle2D(w_global, (h_global * 2.5) / 5);
	/*fin gérer distortion*/

	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity(); // Initialisation de la matrice de modelview
}

/* Méthode permettant de dessiner le pacman */
void View2D::dessinePacman() {
	PacMan pacman = model->getPacMan();
	glPushMatrix();
	glRotatef(pacman.getSpin(), 0.0, 0.0, 1.0); // Applique une rotation selon l'angle récupéré dans le modèle
	if (pacman.isModeSuper()) {
		glColor3f(1.0, 0.0, 0.0);
	} else { //mode normal
		glColor3f(1.0, 0.8, 0.0);
	}
	gluPartialDisk(quadDisque, 0.0, CELL_SIZE / 2.0, 100.0, 1.0, tempAngleDebut,
			tempAngleEntier);
	glPopMatrix();
}

/*
 * Méthode permettant de dessiner un ghost(voiture)
 */
void View2D::dessineGhost(float r, float g, float b) {
	glScaled(0.6, 0.6, 0.6); //diminue les dimension
	glTranslated(-1.3, -0.7, 0); //pk 2 translate avec des valeurs bizarres ?
	glPushMatrix();
	glColor3f(r, g, b);
	glTranslated(1.3, 1.2, 0);

	// dessine gyrophare
	gluDisk(quadDisque, 0.0, CELL_SIZE / 5.0, 100.0, 1.0);
	glPopMatrix();

	// dessine roue gauche
	glPushMatrix();
	glColor3f(0.9, 0.9, 0.9);
	glTranslated(0.8, 0.2, 0);
	gluDisk(quadDisque, 0.0, CELL_SIZE / 3.5, 100.0, 1.0);
	glPopMatrix();

	// dessine roue droite
	glPushMatrix();
	glColor3f(0.9, 0.9, 0.9);
	glTranslated(1.8, 0.2, 0);
	gluDisk(quadDisque, 0.0, CELL_SIZE / 3.5, 100.0, 1.0);
	glPopMatrix();

	// dessine voiture
	glColor3f(1.0, 1.0, 1.0);
	glRectf(0.3, 0.3, 0.6, 0.6);
	glRectf(2.0, 0.3, 2.3, 0.6);

	glBegin(GL_POLYGON);

	glVertex2f(1.0, 1.0);
	glVertex2f(0.6, 0.6);
	glVertex2f(0.6, 0.3);
	glVertex2f(2.0, 0.3);

	glVertex2f(2.0, 0.6);

	glVertex2f(1.6, 1.0);

	glEnd();

	// dessine une bande pointillée sur le coté de la voiture
	glEnable(GL_LINE_STIPPLE);
	glLineStipple(1, 14649);
	//	glLineWidth(2.0);
	glBegin(GL_LINES);

	glColor3f(1.0, 0.6, 0.0);

	glVertex2f(0.5, 0.45);
	glVertex2f(2.3, 0.45);

	glEnd();

	glDisable(GL_LINE_STIPPLE);
}

/*
 * Méthode permettant de dessiner une bière
 */
void View2D::dessineBiere() {

	//dessine la mousse
	glPushMatrix();

	glScalef(0.1, 0.1, 0.1);

	glColor3f(1.0, 1.0, 1.0);

	glRectf(-3., 0.0, 3.0, 2.0);

	glColor3f(1.0, 1.0, 0.23);

	//dessine le verre
	glBegin(GL_POLYGON);

	glVertex2f(-3.00, 0);
	glVertex2f(-1.5, -4.0);

	glVertex2f(1.5, -4.0);
	glVertex2f(3.0, 0);

	glEnd();

	glPopMatrix();

}

/*
 * Méthode permettant de dessiner une bouteille de Whiskey (Super Pastille)
 */
void View2D::dessineJackie() {

	glPushMatrix();

	glColor3f(0.0, 0.0, 0.0);
	glRectf(-0.10, 0.40, 0.10, 0.30);

	glColor3f(0.96, 0.47, 0.0);
	glRectf(-0.10, 0.30, 0.10, 0.15);
	glRectf(-0.20, 0.15, 0.20, 0.05);

	glColor3f(0.0, 0.0, 0.0);
	glRectf(-0.20, 0.05, 0.20, -0.20);

	glColor3f(0.96, 0.47, 0.0);
	glRectf(-0.20, -0.20, 0.20, -0.35);

	glPopMatrix();

}

/*
 * dessine un mur interieur
 */
void View2D::dessineMurInterieur() {
	//variables utilisées pour dessiner les murs
	float x1 = -CELL_SIZE / 2; //coin inférieur gauche X
	float x2 = CELL_SIZE / 2; //coin supérieur droit X
	float y1 = -CELL_SIZE / 2; //coin inférieur gauche Y
	float y2 = CELL_SIZE / 2; //coin supérieur droit Y
	glColor3f(0.76, 0.76, 0.76);
	glRectf(x1, y1, x2, y2);
}
/*
 * dessine un mur extérieur horizontal
 */
void View2D::dessineMurExHor() {
	//variables utilisées pour dessiner les murs
	float x1 = -CELL_SIZE / 2; //coin inférieur gauche X
	float x2 = CELL_SIZE / 2; //coin supérieur droit X
	float y1 = -CELL_SIZE / 2; //coin inférieur gauche Y
	float y2 = CELL_SIZE / 2; //coin supérieur droit Y
	glColor3f(0.76, 0.76, 0.76);
	glBegin(GL_LINE_LOOP); //dessine une ligne coupant en 2 la cellule horizontalement
	glVertex2f(x1, (y1 + y2) / 2);
	glVertex2f(x2, (y1 + y2) / 2);
	glEnd();
}

/*
 * dessine un mur extérieur vertical
 */
void View2D::dessineMurExVert() {
	//variables utilisées pour dessiner les murs
	float x1 = -CELL_SIZE / 2; //coin inférieur gauche X
	float x2 = CELL_SIZE / 2; //coin supérieur droit X
	float y1 = -CELL_SIZE / 2; //coin inférieur gauche Y
	float y2 = CELL_SIZE / 2; //coin supérieur droit Y
	glColor3f(0.76, 0.76, 0.76);
	glBegin(GL_LINE_LOOP); //dessine une ligne coupant en 2 la cellule verticalement
	glVertex2f((x1 + x2) / 2, y1);
	glVertex2f((x1 + x2) / 2, y2);
	glEnd();
}
/*
 * dessine le coin d'un angle.
 * Pas stocké dans une display list car nous avons que les
 * coins soient dessinés en immediate mode.
 * Nous nous permettons de faire cela car cet un élément graphique qui ne
 * sera dessiné que 4 fois toutes les itérations
 */
void View2D::dessineCoinExt(int i, int j) {
	//variables utilisées pour dessiner les murs
	float x1 = -CELL_SIZE / 2; //coin inférieur gauche X
	float x2 = CELL_SIZE / 2; //coin supérieur droit X
	float y1 = -CELL_SIZE / 2; //coin inférieur gauche Y
	float y2 = CELL_SIZE / 2; //coin supérieur droit Y
	glColor3f(0.76, 0.76, 0.76);
	glRectd(x1, y1, x2, y2);
}

/*
 * dessine mur de prison
 */
void View2D::dessineMurPrison() {
	//variables utilisées pour dessiner les murs
	float x1 = -CELL_SIZE / 2; //coin inférieur gauche X
	float x2 = CELL_SIZE / 2; //coin supérieur droit X
	float y1 = -CELL_SIZE / 2; //coin inférieur gauche Y
	float y2 = CELL_SIZE / 2; //coin supérieur droit Y
	glColor3f(0.0, 1.0, 0.0);
	glBegin(GL_LINE_LOOP); // dessine rectangle non rempli pour répresenter un mur de prison
	glVertex2f(x1, y1);
	glVertex2f(x2, y1);
	glVertex2f(x2, y2);
	glVertex2f(x1, y2);
	glEnd();
}

void View2D::gererProjection() {
	//not implemented
}

void View2D::gererLookAt() {
	//not implemented
}
/*
 * dessine un point au centre du plateau permettant de faire une rotation sur le plateau
 */
void View2D::dessineMiddlePoint() {
	//pas besoin d'implementer cette méthode pour la 2D
}
/*
 * Méthode permettant d'appliquer une décalage sur
 * l'animation de la bouche du pacman de sorte qu'au final avec plusieurs décalages,
 * l'animation donne l'impression d'avoir un mouvement continu
 */
void View2D::applySmoothPacmanAnimation(float smoothLag) {
	PacMan pacman = model->getPacMan();
	float angleDebut_from;
	float angleDebut_to;
	float angleEntier_from;
	float angleEntier_to;
	if (pacman.getAngleBoucheDebut() == ANGLEDEPARTFERME) { //il faut aller de -80 à -40 et de 350 à 250
		angleDebut_from = ANGLEDEPARTFERME;
		angleDebut_to = ANGLEDEPARTOUVERT;
		angleEntier_from = ANGLEENTIERFERME;
		angleEntier_to = ANGLEENTIEROUVERT;
	} else { // de - 40 à 80 et de 250 à 350
		angleDebut_from = ANGLEDEPARTOUVERT;
		angleDebut_to = ANGLEDEPARTFERME;
		angleEntier_from = ANGLEENTIEROUVERT;
		angleEntier_to = ANGLEENTIERFERME;
	}
	/*
	 * incrémente pas à pas l'angle qui sera étalonné sur 'SMOOTHLAG(10)' pas afin d'arriver à l'autre état
	 * ex: pour -80: -76,-72,... jusque -40
	 */
	float smoothAngleDebut = angleDebut_to
			- ((angleDebut_to - angleDebut_from) * smoothLag);
	float smoothAngleEntier = angleEntier_to
			- ((angleEntier_to - angleEntier_from) * smoothLag);

	tempAngleDebut = smoothAngleDebut; //set nouvelle valeur pour dessin
	tempAngleEntier = smoothAngleEntier; //set nouvelle valeur pour dessin
}

/*
 * Distortion appliquer à la viewport du millieu en 2D
 */
void View2D::distortionMiddle2D(float w, float h) {
	d1 = -model->getLabyrinthe()->getMatrixWidth() / 1.98;
	delta1 = model->getLabyrinthe()->getMatrixWidth();

	d2 = -model->getLabyrinthe()->getMatrixHeight() / 2;
	delta2 = model->getLabyrinthe()->getMatrixHeight();
	/*gérer distortion*/
	if ((w / h) > (delta1 / delta2))
		gluOrtho2D(d1 - (1. / 2.) * (((w * delta2) / h) - delta1),
				d1 + delta1 + (1. / 2.) * (((w * delta2) / h) - delta1), d2,
				d2 + delta2);

	if ((w / h) <= (delta1 / delta2))
		gluOrtho2D(d1, d1 + delta1, d2 - 1. * (((h * delta1) / w) - delta2),
				d2 + delta2 + 0 * (((h * delta1) / w) - delta2));
	/*fin gérer distortion*/
}
